'''
Created on Aug 8, 2013
Python 3.3.2
@author: Nick
'''

class circuit():
    def __init__(self, id_number):
        '''This class contains all the information for the gates, inputs, and outputs created,
         will also handle most of the simulation'''
        self.__id_number = id_number
        self.__gates = []
        self.__inputs = []
        self.__outputs = []
    def addGate(self, gate_type):
        '''This function will create a new gate and add it to the circuit'''
        self.__gates.append(gates(len(self.__gates)+1, gate_type))
    def removeGate(self, gate_id_number):
        '''This function removes a gate with the give gate id number'''
        self.__gates.pop(self.findItem("GATE", gate_id_number)[0])
    def addInput(self, input_value=0):
        '''This function adds an input to the circuit'''
        self.__inputs.append(inputs(len(self.__inputs)+1, input_value))
    def removeInput(self, input_id_number):
        '''This function removes an input with the given input id number'''
        self.__inputs.pop(self.findItem("INPUT", input_id_number)[0])
    def setInput(self, input_id_number, value):
        self.__inputs[self.findItem("INPUT", input_id_number)[0]].setInput_Value(value)
    def addOutput(self):
        '''This function adds an output to the circuit'''
        self.__outputs.append(outputs(len(self.__outputs)+1))
    def removeOutput(self, output_id_number):
        '''This function removes an output with the give output id number'''
        self.__outputs.pop(self.findItem("OUTPUT", output_id_number)[0])
    def wire(self, outputItem1Type, inputItem2Type, item1Id_Number, item2Id_Number,):
        '''This function connects a gates output to a gate input'''
        output_item = self.findItem(outputItem1Type, item1Id_Number)[1]
        input_item = self.findItem(inputItem2Type, item2Id_Number)[1]
        if inputItem2Type == "GATE":
            input_item.addGate_Inputs(output_item)
        elif inputItem2Type == "OUTPUT":
            input_item.addOutput_Input(output_item)
    def findItem(self, itemType, itemId_Number):
        '''This function will return the item asked for based on its type and id number'''
        if itemType == "GATE":
            for i in range(len(self.__gates)):
                if itemId_Number == self.__gates[i].getGateId_Number():
                    return [i, self.__gates[i]]
        if itemType == "INPUT":
            for i in range(len(self.__inputs)):
                if itemId_Number == self.__inputs[i].getInputId_Number():
                    return [i, self.__inputs[i]]
        if itemType == "OUTPUT":
            for i in range(len(self.__outputs)):
                if itemId_Number == self.__outputs[i].getOutputId_Number():
                    return [i, self.__outputs[i]]
    def simulate(self):
        '''This function simulates the circuit based on given inputs'''
        layers = [[],]
        
        for i in range(len(self.__inputs)):
            layers[0].append(self.__inputs[i])
        print(layers)
        
        counted_outputs = 0
        layers_spot = 0
        working_spot = 0
        while counted_outputs < len(self.__outputs):
            layers.append([])
            
            print(layers)
            for i in range(len(self.__gates)):
                for i in range(len(layers[layers_spot])):
                    ''' '''
            for i in range(len(self.__outputs)):
                for i in range(len(layers[layers_spot])):
                    ''' '''
            layers_spot = layers_spot + 1
    def truthtable(self):
        '''This function makes a truth table based on all possible inputs'''
    def __str__(self):
        output = ""
        output = output + "The id number of the circuit is: " + str(self.__id_number)
        output = output + "\n This circuits contains these gates: "
        for i in range(len(self.__gates)):
            output = output + str(self.__gates[i].getGateId_Number()) + ", "
        output = output + "\n This circuits contains these inputs: "
        for i in range(len(self.__inputs)):
            output = output + str(self.__inputs[i].getInputId_Number()) + ", "
        output = output + "\n This circuits contains these outputs: "
        for i in range(len(self.__outputs)):
            output = output + str(self.__outputs[i].getOutputId_Number()) + ", "
        return output
class gates():
    def __init__(self, id_number, gate_type="AND", gate_inputs=[]):
        '''This class creates gates with a type that have input and outputs'''
        self.__id_number = id_number
        self.__gate_type = gate_type
        self.__gate_inputs = gate_inputs
        self.__gate_value = 0
    def compute(self, input_values):
        '''This function will return the value of the output of the gate based on gate type and input values'''
        gate_type =  self.__gate_type
        if gate_type == "AND":
            for i in range(len(input_values)):
                if input_values[i] == 0:
                    return 0
            return 1
        if gate_type == "OR":
            for i in range(len(input_values)):
                if input_values[i] == 1:
                    return 1
            return 0
        if gate_type == "INVERTER":
            value = 1
            for i in range(len(input_values)):
                value = value * input_values[i]
            if value == 1:
                return 0
            if value == 0:
                return 1
        if gate_type == "XOR":
            count = 0
            for i in range(len(input_values)):
                if input_values[i] == 1:
                    count = count + 1
            if count == 1:
                return 1
            else:
                return 0
        if gate_type == "NAND":
            for i in range(len(input_values)):
                if input_values[i] == 0:
                    return 1
            return 0
        if gate_type == "NOR":
            for i in range(len(input_values)):
                if input_values[i] == 1:
                    return 0
            return 1
        if gate_type == "NXOR":
            count = 0
            for i in range(len(input_values)):
                if input_values[i] == 1:
                    count = count + 1
            if count == 1:
                return 0
            else:
                return 1
    def getGate_Type(self):
        '''This function returns the type of gate as string'''
        return self.__gate_type
    def setGate_Type(self, gate_type="AND"):
        '''This function changes the type of the gate'''
        self.__gate_type = gate_type
    def addGate_Inputs(self, Item):
        '''Adds to the list of inputs'''
        self.__gate_inputs.append(Item)
    def clearGate_Inputs(self):
        '''Clears the list of inputs into a gate'''
        self.__gate_inputs = []
    def getGate_Inputs(self):
        '''This function returns the gates inputs'''
        return self.__gate_inputs
    def getGate_Value(self):
        '''This function returns the gates value'''
        return self.__gate_value
    def setGate_Value(self, gate_value):
        '''This function sets the gates value'''
        self.__gate_value = gate_value
    def getGateId_Number(self):
        '''This function returns the id number of the gate'''
        return self.__id_number
class inputs():
    def __init__(self, id_number, input_value=0):
        '''This class makes inputs'''
        self.__id_number = id_number
        self.__input_value = input_value
    def getInput_Value(self):
        '''This function returns the inputs value'''
        return self.__input_value
    def setInput_Value(self, input_value=0):
        '''This function sets the binary value of the input'''
        self.__input_value = input_value
    def getInputId_Number(self):
        '''This function returns the id_number of the input'''
        return self.__id_number
class outputs():
    def __init__(self, id_number, output_input=[]):
        '''This class makes outputs'''
        self.__id_number = id_number
        self.__output_inputs = output_input
        self.__ouput_value = 0
    def addOutput_Input(self, item):
        '''This function adds an input'''
        self.__output_inputs.append(item)
    def clearOutput_Input(self):
        '''This function clears all inputs'''
        self.__output_inputs = []
    def getOutput_Inputs(self):
        '''This function returns the outputs inputs'''
        return self.__output_inputs
    def getOutput_Value(self):
        '''This function returns the outputs value'''
        return self.__ouput_value
    def setOutput_Value(self, output_value=0):
        '''This functions set the binary output of the circuit'''
        self.__ouput_value = output_value
    def getOutputId_Number(self):
        '''This function returns the id number of the output'''
        return self.__id_number